/**
 * 
 */
package lelouet.datacenter.simulation.vms;

import java.util.Collections;
import java.util.List;

import lelouet.datacenter.simulation.Event;
import lelouet.datacenter.simulation.Hypervisor;
import lelouet.datacenter.simulation.VM;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A migration is a VM that have to put a VM from one hypervisor to another.
 * 
 * @author lelouet
 * 
 */
public class MigrationVM extends HPC {

	private final static Logger mlogger = LoggerFactory
			.getLogger(MigrationVM.class);

	private MigrationVM twin;

	final Hypervisor from;
	final Hypervisor to;
	final VM target;

	/**
	 * @param from
	 *            the {@link Hypervisor} to remove the {@link VM} from.
	 * @param to
	 *            the {@link Hypervisor} that will receive the {@link VM}.
	 * @param target
	 *            the {@link VM} to migrate
	 * @param time
	 *            present time in ms.
	 * @param instructions
	 *            the million instruction this and its {@link #getTwin()} have
	 *            to do before the migration is complete.
	 */
	public MigrationVM(Hypervisor from, Hypervisor to, VM target, long time,
			long instructions) {
		super("migration " + target.getId() + ":" + from.getId() + "->"
				+ to.getId(), time, 0, instructions);
		this.from = from;
		this.to = to;
		this.target = target;
	}

	/** create a Migration that is twined on another one. */
	protected MigrationVM(MigrationVM twin) {
		this(twin.from, twin.to, twin.target, twin.getTime(),
				twin.remaininginstructions);
		this.twin = twin;
	}

	/**
	 * @return the twin {@link MigrationVM} of this. the migration will not be
	 *         finished before this and its twin have done their jobs
	 */
	public MigrationVM getTwin() {
		if (twin == null) {
			twin = new MigrationVM(this);
		}
		return twin;
	}

	/** On job done, apply the migration. */
	@Override
	protected List<Event> onJobDone() {
		mlogger.debug("migration part of <{}> done, twin done : {}",
				new Object[] { this, getTwin().isFinished(),
						getTwin().remaininginstructions });
		if (getTwin().isFinished()) {
			if (from.suspendedVMs().contains(target)) {
				to.suspendVM(target);
			} else {
				to.startVM(target);
			}
			from.removeVM(target);
			from.removeVM(this);
			from.removeVM(getTwin());
			to.removeVM(this);
			to.removeVM(getTwin());
		}
		return Collections.emptyList();
	}

}
